package manager;

import dao.FileDao;
import dao.FileMeta;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

// 针对目录进行扫描，并把扫描的结果同步更新到数据库中
public class FileManager {

    private FileDao fileDao = new FileDao();

    // 初始化设置选手数目为 1，当线程池执行完所有的任务之后，就立即调用一次撞线
    private CountDownLatch countDownLatch =  null;
    // 用于衡量任务结束的计数器
    private AtomicInteger taskCount = new AtomicInteger(0);

    // scanAll方法实现针对 basePath(基准目录) 描述的内容或路径进行扫描
    // 扫描basePath下所包含的文件和子目录,并将这些内容保存到数据库中,该方法考虑子目录内的内容
    public void scanAll(File basePath){
        System.out.println("[FileManager] scanAll 开始！");
        long start = System.currentTimeMillis();

        // 保证每次调用scanAll,重新创建countDown进行计数
        countDownLatch = new CountDownLatch(1);

         // scanAllByOneThread(basePath);
        scanAllByThreadPool(basePath);
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        long end = System.currentTimeMillis();
        System.out.println("[FileManager] scanAll 结束！执行时间：" + (end-start) + "ms");

    }

    // 实现单线程式的递归扫描
    public void scanAllByOneThread(File basePath){
        if(!basePath.isDirectory()){
            return;
        }

        // 针对当前目录进行扫描
        scan(basePath);

        // 列出当前目录下的所有文件
        File[] files = basePath.listFiles();
        if (files == null || files.length == 0){
            // 当前目录下无内容
            return;
        }

        for(File file: files){
            if(file.isDirectory()){
                scanAllByOneThread(file);
            }
        }
    }

    //
    private static ExecutorService executorService = Executors.newFixedThreadPool(10);

    // 利用线程池进行路径扫描
    private void scanAllByThreadPool(File basePath){
        if(!basePath.isDirectory()){
            return;
        }
        // 计数器自增操作
        taskCount.getAndIncrement();

        // 扫描操作，放到线程池里完成
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try{
                    scan(basePath);
                } finally {
                    // 计数器自减操作
                    taskCount.getAndDecrement();
                    if(taskCount.get() == 0){
                        // 若计数器为 0，通知主线程停表
                        countDownLatch.countDown();
                    }
                }
            }
        });

        // 列出当前目录下的所有文件
        File[] files = basePath.listFiles();
        if (files == null || files.length == 0){
            // 当前目录下无内容
            return;
        }

        for(File file: files){
            if(file.isDirectory()){
                scanAllByThreadPool(file);
            }
        }
    }

    // scan方法实现针对一个指定目录进行扫描处理
    // 扫描path下所包含的文件和子目录,并将这些内容保存到数据库中,该方法不考虑子目录内的内容
    public void scan(File path){
        // System.out.println("[FileManager] 扫描路径：" + path.getAbsolutePath());
        // 1, 列出文件系统上真实文件/目录
        List<FileMeta> scanned = new ArrayList<>();
        File[] files = path.listFiles();
        if(files != null){
            for (File file: files) {
                scanned.add(new FileMeta(file));
            }
        }
        // 2,列出数据库中当前指定目录里面的内容
        List<FileMeta> saved = fileDao.searchByPath(path.getPath());

        // 3,删除数据库中存在而当前文件系统中不存在的内容
        List<FileMeta> forDelete = new ArrayList<>();
        for(FileMeta fileMeta : saved){
            if(!scanned.contains(fileMeta)) {
                forDelete.add(fileMeta);
            }
        }
        fileDao.delete(forDelete);

        // 4,添加数据库中不存在而当前文件系统中存在的内容
        List<FileMeta> forInsert = new ArrayList<>();
        for (FileMeta fileMeta: scanned) {
            if(!saved.contains(fileMeta)){
                forInsert.add(fileMeta);
            }
        }
        fileDao.add(forInsert);
    }


}
